using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using MinimizeEngine.Screens;
using MinimizeEngine.Managers;


namespace MinimizeEngine.Networking
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class MEPeerToPeerScreen : MEBaseScreen
    {
        #region Fields

        /// <summary>
        /// Max Number of Total Gamers in the Network Session.
        /// Defaults to 16.
        /// </summary>
        private int mMaxTotalGamers = 16;

        /// <summary>
        /// Max Number of Local Gamers in the Network Session.
        /// Defaults to 4.
        /// </summary>
        private int mMaxLocalGamers = 4;

        /// <summary>
        /// Peer To Peer Network Session.
        /// </summary>
        private NetworkSession mP2PNetworkSession;

        /// <summary>
        /// Peer To Peer Packet Reader.
        /// </summary>
        private PacketReader mP2PPacketReader = new PacketReader();

        /// <summary>
        /// Peer To Peer Packet Writer.
        /// </summary>
        private PacketWriter mP2PPacketWriter = new PacketWriter();

        /// <summary>
        /// string to contain an Networking Error Message.
        /// </summary>
        private string mNetworkErrorMessage;

        #endregion

        #region Properties
        
        /// <summary>
        /// Get or Set the Max Number of Total Gamers in the Network Session
        /// </summary>
        public int MaxTotalGamers
        {
            get { return mMaxTotalGamers; }
            set { mMaxTotalGamers = value; }
        }

        /// <summary>
        /// Get or Set the Max Number of Local Gamers in the Network Session.
        /// Max Number of Local Gamers cannot exceed 4.
        /// </summary>
        public int MaxLocalGamers
        {
            get { return mMaxLocalGamers; }
            set
            {
                if (value > 4)
                    mMaxLocalGamers = 4;
                else
                    mMaxLocalGamers = value; 
            }
        }

        /// <summary>
        /// Get the Peer To Peer Network Session
        /// </summary>
        public NetworkSession P2PNetworkSession
        {
            get { return mP2PNetworkSession; }
        }

        /// <summary>
        /// Creates a new Peer To Peer Networking Session.
        /// </summary>
        private void CreateP2PSession()
        {
            MEScreenManager.DrawMessage("Creating a New Peer To Peer Network Session...", new Vector2(10, 10), Color.Black);

            try
            {
                mP2PNetworkSession = NetworkSession.Create(NetworkSessionType.SystemLink, mMaxLocalGamers, mMaxTotalGamers);
                
                //Hook the Network Session Events now we have Created a New Session.
                HookP2PSessionEvents();
            }
            catch (Exception ex)
            {
                mNetworkErrorMessage = ex.Message;
            }
        }

        /// <summary>
        /// Hook the Peer To Peer Network Session Events
        /// </summary>
        private void HookP2PSessionEvents()
        {
            mP2PNetworkSession.GamerJoined += GamerHasJoinedEventHandler;
            mP2PNetworkSession.SessionEnded += SessionHasEndedEventHandler;
        }

        /// <summary>
        /// This Event Handler function is called when a new Gamer has joined a session.
        /// This allows us to Initialize a new Player in the Game as required.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void GamerHasJoinedEventHandler(object sender, GamerJoinedEventArgs e)
        {

        }

        /// <summary>
        /// This Event Handler function is called when a Peer To Peer Network Session has ended.
        /// We can then dispose of the Network Session properly.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SessionHasEndedEventHandler(object sender, NetworkSessionEndedEventArgs e)
        {
            //Display the Error Message relating to why the Session has Ended.
            mNetworkErrorMessage = e.EndReason.ToString();

            //Dispose of the Network Session and set it back to Null.
            mP2PNetworkSession.Dispose();
            mP2PNetworkSession = null;
        }

        /// <summary>
        /// Join an existing Peer To Peer Networking Session.
        /// </summary>
        private void JoinP2PSession()
        {
            MEScreenManager.DrawMessage("Joining a Peer To Peer Network Session...", new Vector2(10, 10), Color.Black);

            try
            {
                //Search for all Available Peer To Peer Network Sessions
                AvailableNetworkSessionCollection availableNetworkSessions = NetworkSession.Find(NetworkSessionType.SystemLink, mMaxLocalGamers, null);

                if (availableNetworkSessions.Count == 0)
                {
                    //If there are no Available Network Sessions, the tell the Player and return.
                    mNetworkErrorMessage = "There are no available Peer To Peer Network Sessions. Returning...";
                    return;
                }
                else
                {
                    //Join the first Network Session that we find.
                    mP2PNetworkSession = NetworkSession.Join(availableNetworkSessions[0]);

                    //Hook the Peer To Peer Network Session Events.
                    HookP2PSessionEvents();
                }
            }
            catch (Exception ex)
            {
                mNetworkErrorMessage = ex.Message;
            }
        }
        
        #endregion

        #region Initialization

        public MEPeerToPeerScreen()
        {

        }

        #endregion

        #region Handle Input

        /// <summary>
        /// Handle the Input for this Screen
        /// </summary>
        /// <param name="inputManager">Input Manager to get Input States from</param>
        public override void HandleInput(MEInputManager inputManager)
        {
            if (inputManager.IsPause())
            {
                MEEngineManager.Game.Exit();
            }
        }

        #endregion

        #region Update

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            if(this.mP2PNetworkSession == null)
            {
                //If a Network Session has not been created, and we are not in one,
                //then we need to create one.
                UpdateLobbyScreen();
            }
            else
            {
                //We are in a Network Session, so it needs to be updated.
                UpdateP2PNetworkSession();
            }
        }

        /// <summary>
        /// Update the Lobby Screen so that we can Join or Create a Network Session
        /// </summary>
        private void UpdateLobbyScreen()
        {
            //If the Game Is Active (we have focus)
            if(MEEngineManager.Game.IsActive)
            {
                //Check to see if we have any Signed In Gamers
                if (Gamer.SignedInGamers.Count == 0)
                {
                    //If we have no Signed In Gamers, then show the Guide so that
                    //they can sign in.
                    Guide.ShowSignIn(mMaxLocalGamers, false);
                }
                else if (MEEngineManager.Input.IsNewKeyPress(Keys.A) || MEEngineManager.Input.IsNewButtonPress(Buttons.A))
                {
                    //If the Players have signed in and Pressed A, then we can Create a New Peer To Peer Network Session.
                    CreateP2PSession();
                }
                else if (MEEngineManager.Input.IsNewKeyPress(Keys.B) || MEEngineManager.Input.IsNewButtonPress(Buttons.B))
                {
                    //If the Players have signed in and Pressed B, then we can start Searching to Join a Peer To Peer Network Session.
                    JoinP2PSession();
                }
            }
        }

        /// <summary>
        /// Update our Peer To Peer Network Session
        /// </summary>
        private void UpdateP2PNetworkSession()
        {
            //Update all of the Local Players, so that all of the information is sent to everyone
            //in the session.
            foreach (LocalNetworkGamer localGamer in mP2PNetworkSession.LocalGamers)
            {
                //Update the Local Gamer
            }

            //Update the base Network Session
            mP2PNetworkSession.Update();

            //Ensure that the Network Session has not failed or ended.
            if(mP2PNetworkSession == null)
            {
                return;
            }

            //Read all of the Incoming Packets from all Local Gamers, so that each Gamer knows teh stats of all
            //the other gamers.
            foreach (LocalNetworkGamer localGamer in mP2PNetworkSession.LocalGamers)
            {
                //Read all Incoming Packets.
            }
        }

        #endregion

        #region Draw

        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            if(mP2PNetworkSession == null)
            {
                //If the Network Session is Null, then we shall just draw the Lobby Screen
                //so that the Players can Create or Join a Session.
                DrawLobbyScreen();
            }
            else
            {
                //Otherwise we are in a Network Session, and we need to Draw it.
                DrawP2PNetworkSession();
            }
        }

        /// <summary>
        /// Draw the Lobby Screen for the Peer To Peer Network Session
        /// </summary>
        private void DrawLobbyScreen()
        {
            string message = string.Empty;

            //Check to see if we have any Error Messages to display.
            if(!string.IsNullOrEmpty(mNetworkErrorMessage))
            {
                //message += "Network Error:\n" + mNetworkErrorMessage.Replace(". ", ".\n") + "\n\n";
                message += "Network Error:\n" + mNetworkErrorMessage + "\n\n";
            }

            message += "Press A to Create a new Peer To Peer Network Session\n" +
                "Press B to Join an existing Peer To Peer Network Session";

            MEScreenManager.DrawMessage(message, new Vector2(100, 100), Color.Black);
        }

        /// <summary>
        /// Draw the Peer To Peer Network Session.
        /// </summary>
        private void DrawP2PNetworkSession()
        {
            int gamerTagOffset = 0;
            foreach (NetworkGamer networkGamer in mP2PNetworkSession.AllGamers)
            {
                //Here we need to Draw each Player.
                //Once a Player Class has been created in your Game,
                //you can get the Player as follows, and the draw them by calling thier
                //Draw function.
                //
                //Player player = networkGamer.Tag as Player;
                //
                //What this does is get the Player Class that you associated with the Network Gamer
                //when you create them.

                //We can also Draw their GamerTags
                string gamerTag = networkGamer.Gamertag;

                //Check to see if the current Gamer is the Network Session Host.
                if(networkGamer.IsHost)
                {
                    gamerTag += "(Host!)";
                }

                //Other advanced features can be integrated as well.
                //For instance, if we want to display that the Gamer is talking, we can access
                //their IsTalking variable in the networkGamer object, and then do something
                //with this. For example, we can display the Gamer Tag in Red, or draw
                //an icon next to their name.

                MEScreenManager.DrawMessage(gamerTag, new Vector2(10, 10 + gamerTagOffset), Color.Black);
                gamerTagOffset += 10;
            }
        }
        
        #endregion
    }
}